Fix #13805: phpstan does not recognize keys after array literal merge correctly#4897
Merged
ondrejmirtes merged 2 commits into2.1.xfrom Feb 12, 2026
Merged
Conversation
…ray literals - Track HasOffsetValueType accessories when spreading constant arrays with string keys in InitializerExprTypeResolver::getArrayType() - When the array builder degrades to a general array (e.g. due to a non-constant spread), intersect the result with HasOffsetValueType for keys from constant array spreads that appear later - Properly invalidate tracked offsets when a subsequent non-constant spread could overwrite them - New regression test in tests/PHPStan/Analyser/nsrt/bug-13805.php
Closes phpstan/phpstan#13805 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When using array literal syntax with spread operators (
[...$a, ...$b]), PHPStan lost specific key information from constant array types when the array builder was degraded to a general array. This caused false positives when passing the result to functions expecting specific keys.For example,
[...($defaultItems['test'] ?? []), ...$row]where$rowisarray{foo: string, muh: string}was typed asnon-empty-array<string, mixed>instead of preserving the knowledge that keysfooandmuhdefinitely exist with string values.Changes
src/Reflection/InitializerExprTypeResolver.phpin thegetArrayType()method:HasOffsetValueTypeaccessories for non-optional keys when spreading constant arrays with string keysHasOffsetValueTypeentries, intersect them with the result typetests/PHPStan/Analyser/nsrt/bug-13805.phpRoot cause
In
InitializerExprTypeResolver::getArrayType(), when the first spread item is not a single constant array (e.g.,array<string, mixed>), theConstantArrayTypeBuilderis degraded to track only general array types. Subsequent constant array spreads (like...$rowwitharray{foo: string, muh: string}) have their key/value pairs added to the builder, butgetArray()unions all keys and values together, losing the specific offset information.The fix preserves the specific key knowledge by tracking
HasOffsetValueTypeaccessories for non-optional keys from constant array spreads with string keys, and intersecting them with the final degraded array type. This correctly handles ordering: keys from constant arrays spread after non-constant arrays are guaranteed to be present, while keys from constant arrays spread before non-constant arrays are removed if the non-constant array's key type could overwrite them.Test
Added
tests/PHPStan/Analyser/nsrt/bug-13805.phpwhich tests:$defaultItems['test'] ?? []) followed by a constant array ($row) preserves the constant array's key information asHasOffsetValueTypeaccessoriesFixes phpstan/phpstan#13805